//
//  EPEnvironment.m
//  ORM
//
//  Created by PH on 4.2.10.
//  Copyright 2010 Codesign. All rights reserved.
//

#import "EPEnvironment.h"
#import "EPManagedObject.h"
#import "EPTransaction.h"

@implementation EPEnvironment

@synthesize transaction;

- (id)initWithPath:(NSString*)path {
	if ((self = [super init])) {
		objectStore = [[EXObjectStore alloc] initWithPath: path];
		objectsByPointer = [[NSMutableDictionary alloc] init];
	}
	return self;
}

- (EPTransaction*)beginTransaction {
	EPTransaction* _transaction = [[EPTransaction alloc] initWithTransaction: [objectStore beginTransaction] environment: self];
	self.transaction = _transaction;
	[_transaction release];
	return transaction;
}

- (NSException*)performTransaction:(void(^)())block {
	EPTransaction* _transaction = [self beginTransaction];
	@try {
		block();
		[_transaction commit];
		return nil;
	} @catch (NSException* exception) {
		[_transaction rollback];
		return exception;
	}
}

- (id)containerObject {
	if (transaction != nil) return [transaction transaction]; return objectStore;
}

- (id)emptyManagedObjectOfClass:(Class)cls {
	EPManagedObject* managedObject = [[cls alloc] init];
	[objectsByPointer setObject: managedObject forKey: [NSValue valueWithPointer: managedObject]];
	[managedObject release];
	for (NSString* fieldName in [cls ORMPersistentFieldNames]) {
		if ([[cls ORMTransientFieldNames] containsObject: fieldName] == NO) {
			[managedObject addObserver: self forKeyPath: fieldName
							   options: NSKeyValueObservingOptionOld | NSKeyValueObservingOptionNew context: NULL];
		}
	}
	[[self containerObject] storeObject: managedObject];
	return managedObject;
}

- (void)releaseObject:(id)object {
	for (NSString* fieldName in [[object class] ORMPersistentFieldNames]) {
		if ([[[object class] ORMTransientFieldNames] containsObject: fieldName] == NO) {
			[object removeObserver: self forKeyPath: fieldName];
		}
	}
	NSValue* key = [NSValue valueWithPointer: object];
	NSAssert([objectsByPointer objectForKey: key] != nil, @"Object not found in environment's cache");
	[objectsByPointer removeObjectForKey: key];
}

- (void)observeValueForKeyPath:(NSString*)keyPath ofObject:(id)object change:(NSDictionary*)change context:(void*)context {
	if (transaction != nil) {
		id oldValue = [change objectForKey: NSKeyValueChangeOldKey];
		id newValue = [change objectForKey: NSKeyValueChangeNewKey];
		//NSLog(@"Value changed [%@.%@: %@ -> %@]", NSStringFromClass([object class]), keyPath, oldValue, newValue);
		[transaction value: oldValue ofObject: object changedTo: newValue forKeyPath: keyPath];
	}
}

- (void)dealloc {
	NSLog(@"Dealloc: EPEnvironment");
	for (id object in [objectsByPointer allValues]) [self releaseObject: object];
	[objectsByPointer release];
	[objectStore release];
	[transaction release];
	[super dealloc];
}

@end
